﻿namespace OneWorks.Utils.Import.Execl
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using NPOI.HSSF.UserModel;
    using NPOI.SS.UserModel;

    public class ExeclFileHandler<T> where T : new()
    {
        private readonly string _path;
        private readonly Dictionary<string, CustomCellAttribute> _cellAttributes;
        readonly Dictionary<string, string> _propDictionary;

        public ExeclFileHandler(string path)
        {
            _path = path;
            _cellAttributes = new Dictionary<string, CustomCellAttribute>();
            _propDictionary = new Dictionary<string, string>();
            CreateMappers();
        }

        /// <summary>
        /// 创建映射
        /// </summary>
        private void CreateMappers()
        {
            foreach (var prop in typeof(T).GetProperties())
            {
                foreach (CustomCellAttribute cellMapper in prop.GetCustomAttributes(false).OfType<CustomCellAttribute>())
                {
                    _propDictionary.Add(cellMapper.DisplayName, prop.Name);
                    _cellAttributes.Add(cellMapper.DisplayName, cellMapper);
                }
            }
        }

        /// <summary>
        /// 获取整个xls文件对应行的T对象
        /// </summary>
        /// <returns></returns>
        public List<T> ToData()
        {
            List<T> dataList = new List<T>();
            using (FileStream stream = GetStream())
            {
                IWorkbook workbook = new HSSFWorkbook(stream);
                ISheet sheet = workbook.GetSheetAt(0);
                var rows = sheet.GetRowEnumerator();
                int i = 0;
                IRow headRow = null;
                while (rows.MoveNext())
                {
                    var row = sheet.GetRow(i);
                    if (i == 0)
                    {
                        headRow = sheet.GetRow(0);
                    }
                    else
                    {
                        T t = GetData(workbook, headRow, row);
                        dataList.Add(t);
                    }
                    i++;
                }
                stream.Close();
            }
            return dataList;
        }

        /// <summary>
        /// 获取T对象
        /// </summary>
        /// <param name="workbook"></param>
        /// <param name="headRow"></param>
        /// <param name="currentRow"></param>
        /// <returns></returns>
        private T GetData(IWorkbook workbook, IRow headRow, IRow currentRow)
        {
            T t = new T();
            var cells = currentRow.GetEnumerator();
            while (cells.MoveNext())
            {
                ICell cell = cells.Current as ICell;
                if (cell == null) continue;
                var displayName = headRow.GetCell(cell.ColumnIndex).StringCellValue;
                if (!_cellAttributes.ContainsKey(displayName) || !_propDictionary.ContainsKey(displayName))
                {
                    continue;
                }
                var currentAttr = _cellAttributes[displayName];
                var propName = _propDictionary[displayName];
                string value = GetCellValue(workbook, cell);
                if (currentAttr.Type != null)
                {
                    SetValue(ref t, propName, InvokeHandler(currentAttr.Type, value));
                }
                else
                {
                    SetValue(ref t, propName, value);
                }
            }
            return t;
        }

        /// <summary>
        /// 动态执行处理方法
        /// </summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static object InvokeHandler(Type type, object value)
        {
            try
            {
                if (type == null) throw new ArgumentNullException("type");
                System.Reflection.ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
                if (constructor == null) throw new ArgumentNullException("type");
                object mgConstructor = constructor.Invoke(null);
                System.Reflection.MethodInfo method = type.GetMethod("GetResults");
                return method.Invoke(mgConstructor, new[] { value });
            }
            catch (Exception exception)
            {
                throw new Exception(string.Format("{0},{1}", exception.Message, type != null ? type.Name : ""));
            }
        }

        /// <summary>
        /// 获取文件流
        /// </summary>
        /// <returns></returns>
        private FileStream GetStream()
        {
            if (!File.Exists(_path)) throw new FileNotFoundException("path");
            return new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.Read);
        }

        /// <summary>
        /// 获取xls文件单元格的值
        /// </summary>
        /// <param name="workbook"></param>
        /// <param name="cell"></param>
        /// <returns></returns>
        private static string GetCellValue(IWorkbook workbook, ICell cell)
        {
            string value;
            switch (cell.CellType)
            {
                case CellType.FORMULA:
                    HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
                    value = evaluator.Evaluate(cell).FormatAsString();
                    break;
                default:
                    value = cell.ToString();
                    break;
            }
            return value;
        }

        /// <summary>
        /// 设置T属性值
        /// </summary>
        /// <param name="t"></param>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        private static void SetValue(ref T t, string propName, object value)
        {
            var typeName = t.GetType().GetProperty(propName).PropertyType.Name;
            var property = t.GetType().GetProperty(propName);
            switch (typeName)
            {
                case "Int32":
                    property.SetValue(t, Convert.ToInt32(value), null);
                    break;
                case "DateTime":
                    property.SetValue(t, Convert.ToDateTime(value), null);
                    break;
                case "Decimal":
                    property.SetValue(t, Convert.ToDecimal(value), null);
                    break;
                default:
                    property.SetValue(t, value, null);
                    break;
            }
        }
    }
}
